/*
 * Copyright 2022 Institute of Software Chinese Academy of Sciences, ISRC
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "napi/native_api.h"
#include <cstdio>
#include <cstring>
#include <ctime>
#include <js_native_api.h>
#include <js_native_api_types.h>
#include <stdlib.h>
#include <string>
#include <sys/types.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/shm.h>
#include <hilog/log.h>
#include <pthread.h>
#include <cstring>
#include <iostream>

//定义了一些缓冲区的大小。
#define KEY_BUFFER_SIZE 20
#define DATA_BUFFER_SIZE 1024

//用于打印日志的设置，与通信功能无关。
#define LOG_DOMAIN 0x00201
#define LOG_TAG "MY_TAG"

//定义单向链表
class Node {
public:
    char data; // data
    Node* next; // pointer to next
};

//利用链表实现动态队列
class Queue
{
public:
    int counter;// 队列内节点个数
    Queue();// constructor
    ~Queue();// destructor
    bool IsEmpty(void);//队列为空
    void Enqueue(char x);//入队
    void Dequeue(char &x);//出队
    void getcount(int &c);//获得队列内节点个数

private:
    Node* front;// 队首指针
    Node* rear;// 队尾指针

};

Queue::Queue()
{
    front = rear = NULL;
    counter = 0;
}

Queue::~Queue()
{
    char value;
    while (!IsEmpty())
        Dequeue(value);
}

bool Queue::IsEmpty()
{
    if (counter)
        return false;
    else
        return true;
}

void Queue::Enqueue(char x)
{
    Node* newNode = new Node;
    newNode->data = x;
    newNode->next = NULL;
    if (IsEmpty())
    {
        front = newNode;
        rear = newNode;
    } else {
        rear->next = newNode;
        rear = newNode;
    }
    counter++;
    OH_LOG_INFO(LOG_APP,"counter is %{public}d",counter);
}

void Queue::Dequeue(char &x)
{
    if (IsEmpty())
    {
        OH_LOG_INFO(LOG_APP,"Empty");
        x = '\0';
    }
    else
    {
        //OH_LOG_INFO(LOG_APP,"t");
        x= front->data;
        //OH_LOG_INFO(LOG_APP,"t0");
        Node* nextNode = front->next;
        //OH_LOG_INFO(LOG_APP,"t1");
        delete front;
        //OH_LOG_INFO(LOG_APP,"t2");
        front = nextNode;
        counter--;
        OH_LOG_INFO(LOG_APP,"counter is %{public}d",counter);
    }
}



//使用全局变量创建socket，防止被回收。
static int32_t sock_cli = socket(AF_INET,SOCK_STREAM, 0);

//开辟出js与c++的共享内存，c++中每次收到信息将其存到此内存中，js采用轮询方式去读取该内存中的消息。如果读取内存为空字符，则无消息，不需要处理。
//注：此块内存不允许直接访问，通过setSharedMemory(),getSharedMemory()访问。

//使用全局变量创建sharedMessage和Queue，方便后续函数调用
static std::string sharedMessage;
static Queue queue0;


//赋值给共享内存，仅供C++调用。
static void setSharedMemory(char* buf,Queue &queue,int r)
{

    for(int i=0;i<r;i++)
    {
        //OH_LOG_INFO(LOG_APP,"setSharedMemory()");
        OH_LOG_INFO(LOG_APP, "buffer[i] : %{public}c", buf[i]);

        queue.Enqueue(buf[i]);
    }
    OH_LOG_INFO(LOG_APP,"setSharedMemory() done");
}

//数组版getStep
/*static void getStep(char* sharedm,Queue &queue)
{
    int count;
    char value;
    OH_LOG_INFO(LOG_APP,"into the step()");

    count=queue.counter;
    OH_LOG_INFO(LOG_APP,"%{public}d", count);
    //static char* message = new char[count]();
    if(count<=2048){
        OH_LOG_INFO(LOG_APP,"if(count<=8192) ");
        for(int i=0;i<count+1;i++)
        {

            //OH_LOG_INFO(LOG_APP,"getSharedMemory:before0 for");
            queue.Dequeue(value);
            OH_LOG_INFO(LOG_APP,"getSharedMemory:before1 for");
            sharedm[i]=value;
            OH_LOG_INFO(LOG_APP,"getSharedMemory:before2 for");
            //value='\0';
            OH_LOG_INFO(LOG_APP," getSharedMemory:after for");
        }
    } else{
        OH_LOG_INFO(LOG_APP,"else(count<=8192) 。。。");
        for(int i=0;i<2048;i++)
        {
            queue.Dequeue(value);
            sharedm[i]=value;
            //value='\0';
        }
    }
}*/

//从队列中取出
static void getStep(Queue &queue,std::string& mess)
{
    OH_LOG_INFO(LOG_APP,"into the step()");
    int count=queue.counter;//获取队列节点个数
    char value;//取出节点的值
//    bool flag=false;

    OH_LOG_INFO(LOG_APP,"count is %{public}d", count);
    for(int i=0;i<count+1;i++)
    {

        //OH_LOG_INFO(LOG_APP,"getSharedMemory:before0 for");
        queue.Dequeue(value);
        OH_LOG_INFO(LOG_APP,"round is %{public}d",i);
        OH_LOG_INFO(LOG_APP,"getchar %{public}c",value);

//        if(flag == true){
//             if(value == '\0'){
//                 break;
//             }
//            else{
//                flag = false;
//             }
//        }
        mess.push_back(value);
        //条件判断，如果内存中大于一条消息时则先取出第一条消息并停止
        if(value == '}'){
//            flag = true;
            break;
        }
//        OH_LOG_INFO(LOG_APP,"getStep done()");
    }
    OH_LOG_INFO(LOG_APP,"getStep done()");
    OH_LOG_INFO(LOG_APP,"string is %{public}s", mess.c_str());
}


//读取共享内存内容，仅供js调用。
static napi_value Receive(napi_env env, napi_callback_info info)
{
    OH_LOG_INFO(LOG_APP,"entering Receive");
    //bool cut=false;//是否

    //取出一条消息
    getStep(queue0,sharedMessage);
    //将string转换为char*形式以供处理
    const char *c_s=sharedMessage.c_str();

    OH_LOG_INFO(LOG_APP,"sm is %{public}s", sharedMessage.c_str());
    OH_LOG_INFO(LOG_APP,"cs is %{public}s", c_s);

    napi_value result;
    napi_create_string_utf8(env, c_s, sharedMessage.length(), &result);

    OH_LOG_INFO(LOG_APP,"size of message is %{public}d",sharedMessage.length());

    std::string().swap(sharedMessage);//清空字符串


    OH_LOG_INFO(LOG_APP," after napi_create_string_utf8 。。。");

    return result;
}


//开启线程接收消息。
static void *do_thread(void * arg){
    char buffer[1024] = {0};
    while(1)
        {
            int ret ;
            //打印日志代码。
            OH_LOG_INFO(LOG_APP,"do_thread()");

            //socket接收消息
            ret = read(sock_cli , buffer , 1024);
            if(ret <= 0){
                //OH_LOG_INFO(LOG_APP,"ret == %{public}d", ret);
                continue;;
            }

//            buffer[ret] = '\0' ;
            // 打印接收到的长度， 还有buffer里面的内容
            OH_LOG_INFO(LOG_APP, "ret : %{public}d", ret);
            for(int i = 0; i < 1024; i++) {
                OH_LOG_INFO(LOG_APP, "ret : %{public}c", buffer[i]);
            }
            //将读到的消息写入共享内存。
            OH_LOG_INFO(LOG_APP,"before setSharedMemory()");
            setSharedMemory(buffer,queue0,ret);
            //打印日志代码。
            //OH_LOG_INFO(LOG_APP,"aa %{public}s",&buffer);
            OH_LOG_INFO(LOG_APP,"do thread done()");
        }
}


//开启接收线程，执行do_thread函数。
static void startRec(){
    pthread_t tid;
    OH_LOG_INFO(LOG_APP,"startRec()");
    pthread_create(&tid, NULL, do_thread, (void*)sock_cli);
}


//启动socket连接，供js调用。
/**
 * 
 * @param env napi默认参数
 * @param info napi默认参数
 * @return 连接成功返回1，失败返回0。
 */
static napi_value Connect(napi_env env, napi_callback_info info)
{
    OH_LOG_INFO(LOG_APP,"进入连接函数，该日志仅代表成功调用了connect方法，不代表连接成功，供测试用。");

    //获取napi参数到args[2]
//    size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {nullptr};

    //获取参数，必写
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    //获得IP地址
    size_t keyLen = 0;
    char address[KEY_BUFFER_SIZE];
    napi_get_value_string_utf8(env, args[0], address, sizeof(address), &keyLen);

    //获得端口号
    uint32_t port;
    napi_get_value_uint32(env, args[1], &port);
    uint16_t Port = port;


    ///定义sockaddr_in
    struct sockaddr_in servaddr;
    memset(&servaddr, 0, sizeof(servaddr));
    servaddr.sin_family = AF_INET;
    servaddr.sin_port = htons(Port);  //服务器端口
    servaddr.sin_addr.s_addr = inet_addr(address);  //服务器ip，inet_addr用于IPv4的IP转换（十进制转换为二进制）

    //连接服务器
    napi_value result;

    if (connect(sock_cli, (struct sockaddr *)&servaddr, sizeof(servaddr)) < 0)
    {
        napi_create_int32(env, 0, &result);
    }
    else {
        napi_create_int32(env, 1, &result);
        OH_LOG_INFO(LOG_APP,"C++ 接受线程启动");
        startRec();
    }

    //返回连接状态
    return result;
}


//发送消息，供js调用。
/**
 * 
 * @param env napi默认参数
 * @param info napi默认参数
 * @return 发送成功返回1，失败返回0。
 */
static napi_value Send(napi_env env, napi_callback_info info)
{
    //获取napi参数到args[2]
//    size_t requireArgc = 1;
    size_t argc = 1;
    napi_value args[1] = {nullptr};

    //获取参数，必写
    napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);

    //获取消息
    size_t keyLen = 0;
    char data[DATA_BUFFER_SIZE];
    napi_get_value_string_utf8(env, args[0], data, sizeof(data), &keyLen);

    napi_value result;

    //发送消息。
    if(send(sock_cli, data, strlen(data),0) == -1){
        OH_LOG_INFO(LOG_APP,"zjf == send() : -1");
        napi_create_int32(env, 0, &result);
    } else {
        OH_LOG_INFO(LOG_APP,"zjf == send() : !-1");
        napi_create_int32(env, 1, &result);
    }
    //返回发送状态
    return result;
}

//socket关闭。
static napi_value Close(napi_env env, napi_callback_info info)
{
    close(sock_cli);
}

/*static napi_value Receive(napi_env env, napi_callback_info info)
{

    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(7000);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr));

    listen(listen_fd, 5);

    int conn;
    char clientIP[INET_ADDRSTRLEN] = "";
    struct sockaddr_in clientAddr;
    socklen_t clientAddrLen = sizeof(clientAddr);

    conn = accept(listen_fd, (struct sockaddr*)&clientAddr, &clientAddrLen);

    inet_ntop(AF_INET, &clientAddr.sin_addr, clientIP, INET_ADDRSTRLEN);

    char buf[255];
    memset(buf, 0, sizeof(buf));
    int len = recv(conn, buf, sizeof(buf), 0);
    buf[len] = '\0';


    napi_value result;

    napi_create_string_utf8(env, buf, sizeof(buf), &result);
//    close(listenfd);
    //返回连接状态
    return result;
}*/




//napi对外（js）暴露的方法。{connect,send,close,receive}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports)
{
    napi_property_descriptor desc[] = {
        { "connect", nullptr, Connect, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "send", nullptr, Send, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "close", nullptr, Close, nullptr, nullptr, nullptr, napi_default, nullptr },
        { "receive", nullptr, Receive, nullptr, nullptr, nullptr, napi_default, nullptr }
        //{ "receive", nullptr, Receive, nullptr, nullptr, nullptr, napi_default, nullptr }
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

//固定写法。（libsocket可改动根据具体自定义库名）
static napi_module demoModule = {
.nm_version =1,
.nm_flags = 0,
.nm_filename = nullptr,
.nm_register_func = Init,
.nm_modname = "libsocket",
.nm_priv = ((void*)0),
.reserved = { 0 },
};

extern "C" __attribute__((constructor)) void RegisterModule(void)
{
napi_module_register(&demoModule);
}
